﻿using MathNet.Numerics;
using MathNet.Numerics.Providers.FourierTransform;
using MathNet.Numerics.Threading;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using UtilZ.Dotnet.Ex.Model;

namespace UtilZ.Dotnet.Ex.FourierTransform
{
    /// <summary>
    /// Fourier变换类
    /// </summary>
    unsafe public partial class FourierEx
    {

        private static void Inverse(in IQSpanZ<double> spectrum, FourierTransformScaling scaling)
        {
            if (spectrum.Length.IsPowerOfTwo())
            {
                if (spectrum.Length >= 1024)
                {
                    Radix2InverseParallel(spectrum);
                }
                else
                {
                    Radix2Inverse(spectrum);
                }
            }
            else
            {
                BluesteinInverse(spectrum);
            }

            switch (scaling)
            {
                case FourierTransformScaling.SymmetricScaling:
                    HalfRescale(spectrum);
                    break;
                case FourierTransformScaling.BackwardScaling:
                    FullRescale(spectrum);
                    break;
            }
        }






        /// <summary>
        /// Bluestein generic FFT for arbitrary sized sample vectors.
        /// </summary>
        private static void BluesteinInverse(in IQSpanZ<double> spectrum)
        {
            SwapRealImaginary(spectrum);
            BluesteinConvolutionParallel(spectrum);
            SwapRealImaginary(spectrum);
        }

        /// <summary>
        /// Swap the real and imaginary parts of each sample.
        /// </summary>
        /// <param name="samples">Sample Vector.</param>
        private static void SwapRealImaginary(in IQSpanZ<double> samples)
        {
#if CORE
            using var pointer = new UnmanagedMemoryPointer<double>(samples.Length);
            nuint byteLength = new nuint((ulong)(sizeof(double) * samples.Length));
            NativeMemory.Copy(samples.IDataPtr, pointer.DataPtr, byteLength);
            NativeMemory.Copy(samples.QDataPtr, samples.IDataPtr, byteLength);
            NativeMemory.Copy(pointer.DataPtr, samples.IDataPtr, byteLength);      
            
#else
            using (var pointer = new UnmanagedMemoryPointer<double>(samples.Length))
            {
                var byteLength = (System.nuint)(sizeof(double) * samples.Length);
                NativeMemory.Copy(samples.IDataPtr, pointer.DataPtr, byteLength);
                NativeMemory.Copy(samples.QDataPtr, samples.IDataPtr, byteLength);
                NativeMemory.Copy(pointer.DataPtr, samples.IDataPtr, byteLength);
            }
#endif
        }



        /// <summary>
        /// Radix-2 generic FFT for power-of-two sized sample vectors.
        /// </summary>
        private static void Radix2Inverse(in IQSpanZ<double> data)
        {
            Radix2Reorder(data);
            for (var levelSize = 1; levelSize < data.Length; levelSize *= 2)
            {
                for (var k = 0; k < levelSize; k++)
                {
                    Radix2Step(data.IDataPtr, data.QDataPtr, data.Length, 1, levelSize, k);
                }
            }
        }


        /// <summary>
        /// Radix-2 generic FFT for power-of-two sample vectors (Parallel Version).
        /// </summary>
        private static void Radix2InverseParallel(in IQSpanZ<double> data)
        {
            Radix2Reorder(data);

            double* iDataPtr = data.IDataPtr;
            double* qDataPtr = data.QDataPtr;
            int length = data.Length;

            for (var levelSize = 1; levelSize < length; levelSize *= 2)
            {
                var size = levelSize;

                CommonParallel.For(0, size, 64, (u, v) =>
                {
                    for (int i = u; i < v; i++)
                    {
                        Radix2Step(iDataPtr, qDataPtr, length, 1, size, i);
                    }
                });
            }
        }

    }


}
